﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web.Mvc;
using ilgolosone.Persistence.NH.Data;
using ilgolosone.Shared.Data;
using NUnit.Framework;
using StructureMap;

namespace ilgolosone.DependencyResolver.SM.UnitTests
{
    [TestFixture]
    public class ContainerFixture
    {
        private IContainer _container;

        [SetUp]
        public void TestSetUp()
        {
            _container = new Container(new DefaultRegistry());
            Console.WriteLine(_container.WhatDoIHave());
        }

        [TearDown]
        public void TestTearDown()
        {
            _container = null;
        }

        [Test]
        public void verifico_implementazione_dei_tipi()
        {
            _container.Model.HasImplementationsFor(typeof(IControllerFactory)).Should().Be.True();
            _container.Model.HasImplementationsFor(typeof(ISessionCreator)).Should().Be.True();
            _container.Model.HasImplementationsFor(typeof(IUnitOfWork)).Should().Be.True();
            _container.Model.HasImplementationsFor(typeof(IRepository<>)).Should().Be.True();
        }

        [Test]
        public void verifico_che_implementazione_della_unitofwork_sia_fatta_con_nhibernate()
        {
            using (var uow = _container.GetInstance<IUnitOfWork>())
            {
                typeof(INHibernateUnitOfWork).IsAssignableFrom(uow.GetType()).Should().Be.True();
            }
        }

        [Test]
        public void Nello_Stesso_Thread_Di_Esecuzione_La_UnitOfWork_Deve_Essere_La_Stessa()
        {
            IUnitOfWork uow1 = _container.GetInstance<IUnitOfWork>();
            IUnitOfWork uow2 = _container.GetInstance<IUnitOfWork>();
            Assert.AreEqual(uow1, uow2);
            Assert.AreSame(uow1, uow2);
            uow1.Dispose();
            uow2.Dispose();
        }

        [Test]
        public void In_Diversi_Thread_Di_Esecuzione_La_UnitOfWork_e_Diversa_Per_Ogni_Thread()
        {
            IUnitOfWork uow1 = null;
            IUnitOfWork uow2 = null;

            RunInParallel(
                () => uow1 = _container.GetInstance<IUnitOfWork>(), 
                () => uow2 = _container.GetInstance<IUnitOfWork>()
            );

            uow1.Should().Not.Be.Null();
            uow2.Should().Not.Be.Null();
            
            Assert.AreNotEqual(uow1, uow2);
            Assert.AreNotSame(uow1, uow2);
            
            uow1.Dispose();
            uow2.Dispose();
        }
        
        [Test]
        public void verifico_che_la_sessioncreator_sia_singleton()
        {
            ISessionCreator sessionCreator1 = null;
            ISessionCreator sessionCreator2 = null;

            RunInParallel(
                () => sessionCreator1 = _container.GetInstance<ISessionCreator>(), 
                () => sessionCreator2 = _container.GetInstance<ISessionCreator>()
            );

            sessionCreator1.Should().Not.Be.Null();
            sessionCreator2.Should().Not.Be.Null();

            Assert.AreEqual(sessionCreator1, sessionCreator2);
            Assert.AreSame(sessionCreator1, sessionCreator2);
        }

        private static void RunInParallel(params ThreadStart[] actions)
        {
            List<Thread> threads = actions.Select(action => new Thread(action)).ToList();

            // Start them all...
            threads.ForEach(t => t.Start());

            // And wait for them all to finish
            threads.ForEach(t => t.Join());
        }
    }
}
